/*
 * Default text - jQuery plugin for html5 dragging files from desktop to browser
 *
 * Author: Weixi Yen
 *
 * Email: [Firstname][Lastname]@gmail.com
 *
 * Copyright (c) 2010 Resopollution
 *
 * Licensed under the MIT license:
 *   http://www.opensource.org/licenses/mit-license.php
 *
 * Project home:
 *   http://www.github.com/weixiyen/jquery-filedrop
 *
 * Version:  0.1.0
 *
 * Features:
 *      Allows sending of extra parameters with file.
 *      Works with Firefox 3.6+
 *      Future-compliant with HTML5 spec (will work with Webkit browsers and IE9)
 * Usage:
 * 	See README at project homepage
 *
 */
;(function($) {
  // jQuery.event.props.push('dataTransfer')
  var opts = {},
    default_opts = {
      inputFile: '',
      url: '',
      refresh: 1000,
      paramname: 'userfile',
      maxfiles: 25,
      maxfilesize: 1, // MBs
      repeatUpload: false, // 允许重复上传
      data: {},
      drop: empty,
      dragEnter: empty,
      dragOver: empty,
      dragLeave: empty,
      docEnter: empty,
      docOver: empty,
      docLeave: empty,
      beforeEach: empty,
      afterAll: empty,
      rename: empty,
      accessid: '',
      accesskey: '',
      host: '',
      policyBase64: '',
      signature: '',
      callbackbody: '',
      filename: '',
      key: '',
      expire: 0,
      now: Date.now() / 1000,
      // 上传对象名称
      g_object_name: '',
      g_object_name_type: 'random_name',
      max_file_size: '5mb',
      previewThumbUrls: [],
      cbImages: [],

      error: function(err, file, i) {
        alert(err)
      },
      uploadStarted: empty,
      uploadFinished: empty,
      progressUpdated: empty,
      speedUpdated: empty,
      onChange: empty
    },
    errors = ['BrowserNotSupported', 'TooManyFiles', 'FileTooLarge'],
    doc_leave_timer,
    stop_loop = false,
    files_count = 0,
    files

  $.fn.filedrop = function(options) {
    opts = $.extend({}, default_opts, options)
    this.bind('drop', drop)
      .bind('dragenter', dragEnter)
      .bind('dragover', dragOver)
      .bind('dragleave', dragLeave)

    $(document)
      .bind('drop', docDrop)
      .bind('dragenter', docEnter)
      .bind('dragover', docOver)
      .bind('dragleave', docLeave)
      .bind('change', onChange)
  }

  function drop(e) {
    // FIXME: jquery 3.0.0 版本起，终止对 jQuery.event.props 的使用
    // 拖拽框无效，只能拖拽到 input 框内
    return getFileObject(e)
  }

  function getFileObject(e) {
    files = e.target.files || e.dataTransfer.files
    if (files === null || files === undefined) {
      opts.error(errors[0])
      return false
    }

    files_count = files.length
    upload()
    e.preventDefault()
    if(opts.repeatUpload == true && e.target) {
      e.target.value = ''
    }
    return opts.onChange(e)
  }

  function getBuilder(filename, filedata, boundary) {
    var dashdash = '--',
      crlf = '\r\n',
      builder = ''

    $.each(opts.data, function(i, val) {
      if (typeof val === 'function') val = val()
      builder += dashdash
      builder += boundary
      builder += crlf
      builder += 'Content-Disposition: form-data; name="' + i + '"'
      builder += crlf
      builder += crlf
      builder += val
      builder += crlf
    })

    builder += dashdash
    builder += boundary
    builder += crlf
    builder += 'Content-Disposition: form-data; name="' + opts.paramname + '"'
    builder += '; filename="' + filename + '"'
    builder += crlf

    builder += 'Content-Type: application/octet-stream'
    builder += crlf
    builder += crlf

    builder += filedata
    builder += crlf

    builder += dashdash
    builder += boundary
    builder += dashdash
    builder += crlf
    return builder
  }

  // TODO: 上传oss方法
  function oss() {

  }

  function progress(e) {
    if (e.lengthComputable) {
      var percentage = Math.round((e.loaded * 100) / e.total)
      if (this.currentProgress != percentage) {
        this.currentProgress = percentage
        opts.progressUpdated(this.index, this.file, this.currentProgress)

        var elapsed = new Date().getTime()
        var diffTime = elapsed - this.currentStart
        if (diffTime >= opts.refresh) {
          var diffData = e.loaded - this.startData
          var speed = diffData / diffTime // KB per second
          opts.speedUpdated(this.index, this.file, speed)
          this.startData = e.loaded
          this.currentStart = elapsed
        }
      }
    }
  }

  function upload() {
    stop_loop = false
    if (!files) {
      opts.error(errors[0])
      return false
    }
    var filesDone = 0,
      filesRejected = 0

    if (files_count > opts.maxfiles) {
      opts.error(errors[1])
      return false
    }

    for (var i = 0; i < files_count; i++) {
      if (stop_loop) return false
      try {
        if (beforeEach(files[i]) != false) {
          if (i === files_count) return
          var reader = new FileReader(),
            max_file_size = 1048576 * opts.maxfilesize

          reader.index = i
          if (files[i].size > max_file_size) {
            opts.error(errors[2], files[i], i)
            filesRejected++
            continue
          }

          reader.onloadend = send
          reader.readAsBinaryString(files[i])
        } else {
          filesRejected++
        }
      } catch (err) {
        opts.error(errors[0])
        return false
      }
    }

    function ajax_get_signature(cb) {
      $.get(opts.getOssSignUrl, {},
        function (d) {
          if (d.status === 200 && d.data) {
            if(typeof cb === 'function') {
              cb(d.data)
            }
            return d.data
          } else {
            throw new Error('获取签名出错')
          }
        },
        "json"
      );
    },

     // 配置签名
     async get_signature() {
      //可以判断当前expire是否超过了当前时间,如果超过了当前时间,就重新取一下.3s 做为缓冲
      let now = Date.parse(new Date()) / 1000

      if (this.expire < now + 3) {
        await this.ajax_get_signature().then(obj => {
          console.log('​get_signature -> obj', obj)
          this.host = obj['host']
          this.policyBase64 = obj['policy']
          this.accessid = obj['accessid']
          this.signature = obj['signature']
          this.expire = parseInt(obj['expire'])
          this.callbackbody = obj['callback']
          this.key = obj['dir']
        })
      }
    },
    beforeUpload() {
      let ret = false
      this.get_signature().then(res => {
        if (ret === false) {

        }
      })
    },
    set_upload_param() {
      // 顺序不能换
      this.ossSignObj = {
        key: this.g_object_name,
        policy: this.policyBase64,
        OSSAccessKeyId: this.accessid,
        //让服务端返回200,不然，默认会返回204
        success_action_status: '200',
        callback: this.callbackbody,
        signature: this.signature
      }
    },
    random_string(len) {
      len = len || 32
      let chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678'
      let maxPos = chars.length
      let pwd = ''
      for (let i = 0; i < len; i++) {
        pwd += chars.charAt(Math.floor(Math.random() * maxPos))
      }
      return pwd
    },
    get_suffix(filename) {
      let pos = filename.lastIndexOf('.')
      let suffix = ''
      if (pos != -1) {
        suffix = filename.substring(pos)
      }
      return suffix
    },
    calculate_object_name(filename) {
      if (this.g_object_name_type == 'local_name') {
        this.g_object_name += '${filename}'
      } else {
        let suffix = this.get_suffix(filename)
        this.g_object_name = this.key + this.random_string(10) + suffix
        console.log(
          '​calculate_object_name -> this.g_object_name',
          this.g_object_name
        )
      }
      return this.g_object_name
    },
    get_uploaded_object_name(filename) {
      if (this.g_object_name_type == 'local_name') {
        let tmp_name = this.g_object_name
        tmp_name = tmp_name.replace('${filename}', filename)
        return tmp_name
      } else if (this.g_object_name_type == 'random_name') {
        return this.g_object_name
      }
    },

    function postFile() {
      let _this = this
      console.log(this.expire)
      // 等待拿到签名
      this.get_signature().then(() => {
        this.set_upload_param()
        if (this.fileArr.length === 0) {
          return
        }

        let postArr = []
        this.$dialog.loading.open('玩命上传中')
        this.fileArr.forEach((item, index) => {
          this.g_object_name = this.key
          let filename = item.file.name
          this.cbImages.push({
            index,
            filename
          })
          if (filename != '') {
            this.suffix = this.get_suffix(filename)
            this.ossSignObj['key'] = this.calculate_object_name(filename)
          }

          item.formData.append('name', item.file.name)
          item.formData.append('chunk', 0)
          item.formData.append('chunks', 1)
          for (let i in this.ossSignObj) {
            if (this.ossSignObj.hasOwnProperty(i)) {
              item.formData.append(i, this.ossSignObj[i])
            }
          }
          item.formData.append('file', item.file)

          postArr.push(
            http
              .postFormdata(_this.host, item.formData)
              .then(res => {
                this.images.push({
                  url: res.data.imgurl
                })
                return {
                  success: true,
                  index
                }
              })
              .catch(err => {
                console.error(err)
                return {
                  success: false,
                  index
                }
              })
          )
        })
        Promise.all(postArr)
          .then(indexArr => {
            let allSucess = indexArr.every(item => item.success)
            if (allSucess) {
              console.log('所有文件上传文成', indexArr)
              this.$dialog.toast({ mes: '上传完成' })
            } else {
              console.log('部分文件上传失败', indexArr)
              let errFileNumArr = []
              indexArr
                .filter(item => item.success === false)
                .map((item, index) => {
                  console.log('错误的文件编号', item, index)
                  return this.fileArr[item['index']]['file']['name']
                })
                .map(item => {
                  this.$dialog.toast({ mes: `部分文件上传失败${item}` })
                })
            }
            this.$dialog.loading.close()

            this.fileArr = []
          })
          .catch(err => {
            this.$dialog.loading.close()
            throw new Error('上传文件出错')
          })
      })
    }
    function send(e) {
      // Sometimes the index is not attached to the
      // event object. Find it by size. Hack for sure.
      if (e.target.index == undefined) {
        e.target.index = getIndexBySize(e.total)
      }

      var xhr = new XMLHttpRequest(),
        upload = xhr.upload,
        file = files[e.target.index],
        index = e.target.index,
        start_time = new Date().getTime(),
        boundary = '------multipartformboundary' + new Date().getTime(),
        builder

      newName = rename(file.name)
      if (typeof newName === 'string') {
        builder = getBuilder(newName, e.target.result, boundary)
      } else {
        builder = getBuilder(file.name, e.target.result, boundary)
      }

      upload.index = index
      upload.file = file
      upload.downloadStartTime = start_time
      upload.currentStart = start_time
      upload.currentProgress = 0
      upload.startData = 0
      upload.addEventListener('progress', progress, false)

      xhr.open('POST', opts.url, true)
      xhr.setRequestHeader(
        'content-type',
        'multipart/form-data; boundary=' + boundary
      )

      xhr.sendAsBinary(builder)

      opts.uploadStarted(index, file, files_count)

      xhr.onload = function() {
        if (xhr.responseText) {
          var now = new Date().getTime(),
            timeDiff = now - start_time,
            result = opts.uploadFinished(
              index,
              file,
              jQuery.parseJSON(xhr.responseText),
              timeDiff
            )
          filesDone++
          if (filesDone == files_count - filesRejected) {
            afterAll()
          }
          if (result === false) stop_loop = true
        }
      }
    }
  }

  function getIndexBySize(size) {
    for (var i = 0; i < files_count; i++) {
      if (files[i].size == size) {
        return i
      }
    }

    return undefined
  }

  function rename(name) {
    return opts.rename(name)
  }

  function beforeEach(file) {
    return opts.beforeEach(file)
  }

  function afterAll() {
    return opts.afterAll()
  }

  function dragEnter(e) {
    clearTimeout(doc_leave_timer)
    e.preventDefault()
    opts.dragEnter(e)
  }

  function dragOver(e) {
    clearTimeout(doc_leave_timer)
    e.preventDefault()
    opts.docOver(e)
    opts.dragOver(e)
  }

  function dragLeave(e) {
    clearTimeout(doc_leave_timer)
    opts.dragLeave(e)
    e.stopPropagation()
  }

  function docDrop(e) {
    e.preventDefault()
    opts.docLeave(e)
    return false
  }

  function docEnter(e) {
    clearTimeout(doc_leave_timer)
    e.preventDefault()
    opts.docEnter(e)
    return false
  }

  function docOver(e) {
    clearTimeout(doc_leave_timer)
    e.preventDefault()
    opts.docOver(e)
    return false
  }

  function docLeave(e) {
    doc_leave_timer = setTimeout(function() {
      opts.docLeave(e)
    }, 200)
  }

  function empty() {}

  function onChange(e) {
   return getFileObject(e)
  }


  try {
    if (XMLHttpRequest.prototype.sendAsBinary) return
    XMLHttpRequest.prototype.sendAsBinary = function(datastr) {
      function byteValue(x) {
        return x.charCodeAt(0) & 0xff
      }
      var ords = Array.prototype.map.call(datastr, byteValue)
      var ui8a = new Uint8Array(ords)
      this.send(ui8a.buffer)
    }
  } catch (e) {}
})(window.$)
